package com.snake.excel2java.excel.service;

import java.io.File;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.snake.excel2java.excel.IExcelLoader;
import com.snake.excel2java.excel.register.ExcelRegister;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@NoArgsConstructor
public class ExcelLoadService extends Thread {

	@Autowired
	ExcelRegister excelRegister;

	
	//						filename	*Ext
	private static final Map<String, IExcelLoader> loaderMap = new LinkedHashMap<String, IExcelLoader>(256, 0.75f, false);

	private String path;
	private String tempPath;
	private IExcelUpdate excelUpdate;
	private IExcelStop excelStop;
	
	private boolean running = false;
    
    private void register() {
		excelRegister.register(loaderMap);
    }
    
	// path: excel 文件存储路径
	// tempPath: excel 文件临时存储路径，更新上传到此路径下
	// startLine: 文件读取开始行数，默认为4
    public void start(String path, String tempPath, IExcelUpdate excelUpdate, IExcelStop excelStop) {
    	this.path = path;
    	this.tempPath = tempPath;
    	this.excelUpdate = excelUpdate;
    	this.excelStop = excelStop;
    	
    	register();
    	load();
    	loadTemp();
    	start();
    }

	public void startTest(String path, String tempPath, IExcelUpdate excelUpdate, IExcelStop excelStop) {
		this.path = path;
		this.tempPath = tempPath;
		this.excelUpdate = excelUpdate;
		this.excelStop = excelStop;

		register();
		load();
		loadTemp();
	}
    
    // 加载正式文件
    private boolean load() {
    	
    	List<String> list = new LinkedList<String>();
    	for(Map.Entry<String, IExcelLoader> entry: loaderMap.entrySet()) {
    		
    		IExcelLoader loader = entry.getValue();
    		
    		if(!loader.load(path)) 
    			list.add(loader.getFileName());
    		
    	}

    	boolean result = true;
    	if(!list.isEmpty()) {
    		
    		result = false;
    		
    		log.error("load excel failed, include files: ");
    		
    		for(String fileName: list)
    			log.error("load excel failed, file {}", fileName);
    		
    		excelStop.stop();
    		
    	}
    	
    	return result;

    }
    
    // 加载temp文件夹下文件
    private void loadTemp() {
    	
    	File tempDir = new File(tempPath);
    	
    	if(!tempDir.isDirectory())
    		tempDir.mkdirs();
    	
    	List<String> list = new LinkedList<String>();
    	for(File file: tempDir.listFiles()) {
    		
    		String fileName = file.getName();
    		
    		IExcelLoader loader = loaderMap.get(fileName);
    		
    		if(null == loader) {
    			
    			log.error("load excel failed, file {} not found java config......", file.getAbsolutePath());
    			continue;
    		}
    		
    		boolean result = loader.load(tempPath);
    		
    		if(result) {
    			
        		list.add(loader.getJavaName());
        		
        		copyExcel(fileName);
    			
    		} else {
    			
    			log.error("load excel failed, file {} load exception ......", file.getAbsolutePath());
    		}

    		
    	}
    	
    	if(null != excelUpdate)
    		excelUpdate.notifyUpdate(list);

    }
    
	@Override
	public void run() {
		watchExcelTempDir();
	}
    
    private void watchExcelTempDir() {
		
		try {
						
			WatchService watchService = FileSystems.getDefault().newWatchService();
			
			Paths.get(tempPath).register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
			
			log.trace("ExcelLoadService watchFile start to watch excel files....., dir={}", tempPath);
			
			running = true;
			while(running) {
				
				try {
					
		            // 获取下一个文件改动事件
		            WatchKey key = watchService.take();
		            
		            List<String> list = new LinkedList<String>();
		            
		            for (WatchEvent<?> event : key.pollEvents()) {
		            	
		            	String fileName = event.context().toString();
		            	
		            	IExcelLoader loader = loaderMap.get(fileName);
		            	
		            	if(null == loader) {
		            		
		            		log.trace("ExcelLoadService file {} don't has java files", fileName);
		            		
		            		continue;
		            	}
		            	
		            	log.trace("ExcelLoadService start reload excel file {}", fileName);
		            	
		    			// 休眠2秒, 更新有足够的时间收集全部文件信息
		    			try {
		    				Thread.sleep(2000);
		    			} catch (InterruptedException e1) {
		    				// TODO Auto-generated catch block
		    				e1.printStackTrace();
		    			}
		            	
		    			boolean result = loader.load(tempPath);
		    			
		    			if(result) {
			    			list.add(loader.getJavaName());
			    			
			    			copyExcel(loader.getFileName());     
			    			
			    			log.trace("ExcelLoadService reload excel file {}", fileName);
			 	           
		    			} else {
		    				
		    				log.error("failed ExcelLoadService reload excel file {}", fileName);
				 	           
		    			}
		            }
		            
		        	if(null != excelUpdate)
		        		excelUpdate.notifyUpdate(list);
		            
		            // 重设WatchKey
		            boolean valid = key.reset();
		            
		            // 如果重设失败，退出监听
		            if (!valid) {
		            	
		            	log.error("ExcelLoadService reset watch key failed.....");
		            	
		            	throw new Exception("ExcelLoadService watch reset watch key failed.....");
		            }
		            
				} catch (Exception e) {

					log.error("ExcelLoadService watchFile error: message={}.", e.getMessage());
					
					e.printStackTrace();
					
					log.error("ExcelLoadService watchFile restart to watch excel files....., dir={}\r\n\r\n", tempPath);
				}
	
	        }
		
		} catch (Exception e) {

			log.error("ExcelLoadService watchFile error: message={}.", e.getMessage());
			
			e.printStackTrace();
			
			log.error("ExcelLoadService watchFile restart to watch excel files....., dir={}\r\n\r\n", tempPath);
		}
	}
    
    public void shutdown() {
    	running = false;
    }
    
    
    protected void copyExcel(String name) {
    	
    	File file = new File(path + name);
    	
    	// 删除原文件
		if(file.exists())
			file.delete();
		
		File tempFile = new File(tempPath + name);
		
		try {
			
			Files.copy(tempFile.toPath(), file.toPath()); 
				    				
			log.info("copy file {} from {} to {}", name, tempFile.getAbsolutePath(), file.getAbsoluteFile());
			
			tempFile.delete();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			log.error("failed copy file {} from {} to {}", name, tempFile.getAbsolutePath(), file.getAbsoluteFile());
			
			e.printStackTrace();
		}

    }
}
